package com.anivia.common.web.exception;

import cn.dev33.satoken.exception.*;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import com.anivia.common.core.enums.CommonResponseEnum;
import com.anivia.common.core.exception.BusinessException;
import com.anivia.common.core.model.vo.R;
import com.fasterxml.jackson.databind.JsonMappingException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.List;
import java.util.Objects;


/**
 * 全局异常处理,可以捕获更多的异常
 *
 * @author wangj
 */
@Slf4j
@RestControllerAdvice(annotations = {RestController.class, Controller.class})
public class GlobalExceptionHandler {
    /**
     * satoken 异常
     *
     * @param e 业务异常
     * @return {@link R }
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(value = NotLoginException.class)
    public R handlerNotLoginException(NotLoginException e) {
        log.warn("业务异常：", e);
        String msg = switch (e.getType()) {
            case "-1", "-2" -> "登录失效";
            case "-3" -> "登录过期";
            case "-4" -> "账号已在其他地方登录";
            case "-5" -> "账号已被踢下线";
            case "-6" -> "登录异常";
            default -> "未登录";
        };
        return R.fail(msg);
    }

    /**
     * satoken 异常
     *
     * @param e 业务异常
     * @return {@link R }
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(value = {DisableServiceException.class})
    public R handlerDisableServiceException(DisableServiceException e) {
        log.warn("业务异常：", e);
        return R.fail(HttpStatus.FORBIDDEN.value(), "账号被封禁");
    }

    /**
     * satoken 异常
     *
     * @param e 业务异常
     * @return {@link R }
     */
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(value = {NotRoleException.class, NotPermissionException.class})
    public R handlerSaTokenException(SaTokenException e) {
        log.warn("业务异常：", e);
        return R.fail(HttpStatus.FORBIDDEN.value(), "权限不足");
    }

    /**
     * @param e 空指针异常
     * @return {@link R }
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public R handleNullPointerException(NullPointerException e) {
        log.warn("系统异常：", e);
        return R.fail(CommonResponseEnum.NP_EXCEPTION);
    }

    /**
     * @param e 类型转换异常
     * @return {@link R }
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(ClassCastException.class)
    public R handleClassCastException(ClassCastException e) {
        log.warn("系统异常：", e);
        return R.fail(CommonResponseEnum.CLASS_CAST_EXCEPTION);
    }

    /**
     * @param e HTTP请求方式不支持
     * @return {@link R }
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest req) {
        log.warn("系统异常：", e);
        return R.fail(CommonResponseEnum.HTTP_METHOD_NOT_SUPPORT.getCode(), CharSequenceUtil.format(CommonResponseEnum.HTTP_METHOD_NOT_SUPPORT.getDesc(), req.getMethod()));
    }

    /**
     * Http请求消息序列化异常
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public R messageExceptionHandler(HttpMessageNotReadableException e) {
        log.warn("http请求参数转换异常: ", e);
        return R.fail(CommonResponseEnum.ILLEGAL_ARGUMENT);
    }

    /**
     * Http请求参数类型不正确
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public R methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        log.warn("http请求参数类型不匹配: ", e);
        return R.fail(CommonResponseEnum.ILLEGAL_TYER_ERROR.getCode(), StrUtil.format(CommonResponseEnum.ILLEGAL_TYER_ERROR.getDesc(), e.getName()));
    }

    /**
     * 参数绑定异常
     *
     * @param e 异常
     * @return {@link R }
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BindException.class)
    public R handleBindException(BindException e) {
        String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
        log.warn("业务异常：", e);
        return R.fail(CommonResponseEnum.ILLEGAL_ARGUMENT.getCode(), message);
    }

    /**
     * 参数校验异常
     *
     * @param e 异常
     * @return {@link R }
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.warn("业务异常：", e);
        R<String> fail = R.fail(CommonResponseEnum.FEIGN_FAILED.getCode(), CommonResponseEnum.FEIGN_FAILED.getDesc());
        List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
        if (allErrors.size() > 0) {
            fail.setMsg(allErrors.get(0).getDefaultMessage());
        }
        return fail;
    }

    /**
     * 参数校验异常
     *
     * @param e 异常
     * @return {@link R }
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ConstraintViolationException.class)
    public R handleConstraintViolationException(ConstraintViolationException e) {
        log.warn("业务异常：", e);
        String[] arr = e.getMessage().split(":");
        if (arr != null && arr.length >= 2) {
            return R.fail(CommonResponseEnum.ILLEGAL_ARGUMENT.getCode(), arr[1]);
        }
        return R.fail(CommonResponseEnum.ILLEGAL_ARGUMENT.getCode(), e.getMessage());
    }

    /**
     * 参数校验异常
     *
     * @param e 异常
     * @return {@link R }
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(JsonMappingException.class)
    public R handleJsonMappingException(JsonMappingException e) {
        log.warn("业务异常：", e);
        return R.fail(CommonResponseEnum.ILLEGAL_ARGUMENT_JSON);
    }

    @ExceptionHandler(value = {Exception.class})
    @ResponseStatus(HttpStatus.OK)
    public final Object runtimeExceptionHandler(Exception e) {
        if (e instanceof BusinessException) {
            BusinessException businessException = (BusinessException) e;
            log.warn("业务异常：", e);
            return R.fail(CommonResponseEnum.FAILED.getCode(), businessException.getDesc());
        } else {
            log.error("系统异常：", e);
            return R.fail(CommonResponseEnum.SYSTEM_ERROR);
        }
    }
}
